package com.lucifer.treasure.enchant.effect;

import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.minecraft.enchantment.EnchantmentEffectContext;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.enchantment.EnchantmentLevelBasedValue;
import net.minecraft.enchantment.effect.EnchantmentEntityEffect;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Vec3d;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public record BlockReachEnchantmentEffect(EnchantmentLevelBasedValue reach) implements EnchantmentEntityEffect {
    public static final MapCodec<BlockReachEnchantmentEffect> CODEC = RecordCodecBuilder.mapCodec(instance ->
            instance.group(
                    EnchantmentLevelBasedValue.CODEC.fieldOf("reach").forGetter(BlockReachEnchantmentEffect::reach)
            ).apply(instance, BlockReachEnchantmentEffect::new)
    );

    // Unique ID for attribute modifier
    private static final Identifier BLOCK_REACH_MODIFIER_ID = Identifier.of("lucifer_treasure", "block_reach_enchantment");

    // 使用内存存储而不是NBT，避免持久化问题
    private static final Map<UUID, Integer> playerBlockReachLevels = new HashMap<>();

    // Level to reach increase mapping (base reach is about 5, these values are added)
    private static final double[] REACH_LEVELS = {0, 1.0, 2.0, 3.0}; // Level 0-3

    public static void initialize() {
        ServerTickEvents.END_SERVER_TICK.register(server -> {
            server.getPlayerManager().getPlayerList().forEach(player -> {
                updatePlayerBlockReach(player);
            });
        });
    }

    /**
     * Update player's block reach attribute
     */
    private static void updatePlayerBlockReach(PlayerEntity player) {
        int currentLevel = getBlockReachLevelFromChestplate(player);
        int storedLevel = getStoredBlockReachLevel(player);

        // If level changed, update attribute
        if (currentLevel != storedLevel) {
            removeBlockReachModifier(player);

            if (currentLevel > 0) {
                addBlockReachModifier(player, currentLevel);
            }

            setStoredBlockReachLevel(player, currentLevel);
        }
    }

    /**
     * Get Block Reach enchantment level from player's chestplate
     */
    private static int getBlockReachLevelFromChestplate(PlayerEntity player) {
        ItemStack chestplate = player.getEquippedStack(EquipmentSlot.CHEST);
        return getBlockReachLevelFromStack(chestplate);
    }

    /**
     * Get Block Reach enchantment level from item stack
     */
    private static int getBlockReachLevelFromStack(ItemStack stack) {
        if (stack.isEmpty()) {
            return 0;
        }

        var enchantments = EnchantmentHelper.getEnchantments(stack);
        int maxLevel = 0;

        for (var entry : enchantments.getEnchantments()) {
            if (entry.getKey().isPresent()) {
                String enchantmentId = entry.getKey().get().getValue().toString();
                if (enchantmentId.equals("lucifer_treasure:block_reach")) {
                    maxLevel = Math.max(maxLevel, enchantments.getLevel(entry));
                }
            }
        }

        return maxLevel;
    }

    /**
     * Add block reach attribute modifier
     */
    private static void addBlockReachModifier(PlayerEntity player, int level) {
        double reachIncrease = getBlockReachBoost(level);

        // Create attribute modifier
        EntityAttributeModifier reachModifier = new EntityAttributeModifier(
                BLOCK_REACH_MODIFIER_ID,
                reachIncrease,
                EntityAttributeModifier.Operation.ADD_VALUE
        );

        // Add to player's block interaction range (for mining and placing blocks)
        var blockReachAttribute = player.getAttributeInstance(EntityAttributes.PLAYER_BLOCK_INTERACTION_RANGE);
        if (blockReachAttribute != null && !blockReachAttribute.hasModifier(BLOCK_REACH_MODIFIER_ID)) {
            blockReachAttribute.addTemporaryModifier(reachModifier);
        }

        // Also add to entity interaction range (for GUI access like chests, workbenches)
        var entityReachAttribute = player.getAttributeInstance(EntityAttributes.PLAYER_ENTITY_INTERACTION_RANGE);
        if (entityReachAttribute != null && !entityReachAttribute.hasModifier(BLOCK_REACH_MODIFIER_ID)) {
            entityReachAttribute.addTemporaryModifier(reachModifier);
        }
    }

    /**
     * Remove block reach attribute modifier
     */
    private static void removeBlockReachModifier(PlayerEntity player) {
        var blockReachAttribute = player.getAttributeInstance(EntityAttributes.PLAYER_BLOCK_INTERACTION_RANGE);
        if (blockReachAttribute != null) {
            blockReachAttribute.removeModifier(BLOCK_REACH_MODIFIER_ID);
        }

        var entityReachAttribute = player.getAttributeInstance(EntityAttributes.PLAYER_ENTITY_INTERACTION_RANGE);
        if (entityReachAttribute != null) {
            entityReachAttribute.removeModifier(BLOCK_REACH_MODIFIER_ID);
        }
    }

    /**
     * Calculate block reach increase amount
     */
    private static double getBlockReachBoost(int level) {
        if (level <= 0) {
            return 0;
        }
        if (level < REACH_LEVELS.length) {
            return REACH_LEVELS[level];
        }
        // Support for higher levels (e.g. from other mods like Apotheosis)
        int extraLevels = level - REACH_LEVELS.length + 1;
        return REACH_LEVELS[REACH_LEVELS.length - 1] + extraLevels * 0.5;
    }

    /**
     * Get stored block reach level
     */
    private static int getStoredBlockReachLevel(PlayerEntity player) {
        return playerBlockReachLevels.getOrDefault(player.getUuid(), 0);
    }

    /**
     * Set stored block reach level
     */
    private static void setStoredBlockReachLevel(PlayerEntity player, int level) {
        if (level == 0) {
            playerBlockReachLevels.remove(player.getUuid());
        } else {
            playerBlockReachLevels.put(player.getUuid(), level);
        }
    }

    @Override
    public void apply(ServerWorld world, int level, EnchantmentEffectContext context, Entity target, Vec3d pos) {
        // Main logic is handled in server tick event
        // This method is kept for other possible trigger ways
        if (target instanceof PlayerEntity player) {
            updatePlayerBlockReach(player);
        }
    }

    @Override
    public MapCodec<? extends EnchantmentEntityEffect> getCodec() {
        return CODEC;
    }
} 